#!/usr/bin/env python
# -*- coding: iso-8859-1 -*-

# chimera - observatory automation system
# Copyright (C) 2006-2007  P. Henrique Silva <henrique@astro.ufsc.br>

# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License
# as published by the Free Software Foundation; either version 2
# of the License, or (at your option) any later version.

# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
# GNU General Public License for more details.

# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.

import sys
import logging
import optparse
import os

from optparse import OptionParser, OptionGroup

from chimera.core.manager import Manager
from chimera.core.location import Location
from chimera.core.version import _chimera_version_, _chimera_description_
from chimera.core.constants import MANAGER_DEFAULT_HOST, MANAGER_DEFAULT_PORT
from chimera.core.exceptions import ObjectNotFoundException, InvalidLocationException
from chimera.core.path import ChimeraPath
from chimera.core.state    import State
from chimera.core.managerlocator import ManagerLocator, ManagerNotFoundException

from Pyro.util import getPyroTraceback

import chimera.core.log

log = logging.getLogger('chimera.scripts')
chimera.core.log.setConsoleLevel(logging.WARNING)


class doStuff:
    def __init__(self, options, args):
 
        try:
            self.locManager = ManagerLocator.locate()
        except ManagerNotFoundException:
            print >> sys.stderr, "Couldn't find current Chimera instance. Is Chimera running?"
            sys.exit(1)
        
        if options.verbose == 1:
            chimera.core.log.setConsoleLevel(logging.INFO)
            
        if options.verbose > 1:
            chimera.core.log.setConsoleLevel(logging.DEBUG)
    
        self.paths = {"instruments": [],
                      "controllers": [],
                      "drivers": []}

        # add system path
        self.paths["instruments"].append(ChimeraPath.instruments())
        self.paths["controllers"].append(ChimeraPath.controllers())
        self.paths["drivers"].append(ChimeraPath.drivers())

        log.info("Setting objects include path from command line parameters...")
        for _dir in options.inst_dir:
            self.paths["instruments"].append(_dir)
    
        for _dir in options.ctrl_dir:
            self.paths["controllers"].append(_dir)
        
        for _dir in options.drv_dir:
            self.paths["drivers"].append(_dir)
        
        remSite = options.pyro_host + ':' + options.pyro_port.__str__()
        remManagerLoc=remSite + '/Manager/0'

    
        log.debug('Attempting to connect to manager on ' + remManagerLoc)
        try:
            #Must != MANAGER_DEFAULT_PORT to prevent connecting to self!
            rm = self.locManager.getProxy(remManagerLoc)
        except ObjectNotFoundException:
            log.error('Unable to connect to remote manager!')
            self.endMe()
        
        log.info('Connected to ' + rm.URI.__str__())
        
        if options.shutdown:
            log.info('Shutting down ' + rm.URI.__str__() + '...')
            rm.shutdown()
            self.endMe()
            
        
        for co in options.drivers:
            log.info('Loading driver ' + co + '...')
            try:
                rm.addLocation(co, path=self.paths["drivers"], start=True)
            except Exception, e:
                log.warning('Failed to start ' + co + ': ' + e.message)
                print ''.join(getPyroTraceback(e))
        for co in options.instruments:
            log.info('Loading instrument ' + co + '...')
            try:
                rm.addLocation(co, path=self.paths["instruments"], start=True)
            except Exception, e:
                log.warning('Failed to start ' + co + ': ' + e.message)
                print ''.join(getPyroTraceback(e))
        for co in options.controllers:
            log.info('Loading controller ' + co + '...')
            try:
                rm.addLocation(co, path=self.paths["controllers"], start=True)
            except Exception, e:
                log.warning('Failed to start ' + co + ': ' + e.message)
                print ''.join(getPyroTraceback(e))
        
        for co in options.start:
            try:
                log.debug('Attempting to start ' + remSite + co)
                rm.start(co)
                log.info('Started ' + remSite + co)
            except Exception, e:
                log.warning('Unable to start ' + co + ': ' + e.message)
                print ''.join(getPyroTraceback(e))
        
        for co in options.stop:
            try:
                log.debug('Attempting to stop ' + remSite + co)
                rm.stop(co)
                log.info('Stopped ' + remSite + co)
            except Exception, e:
                log.warning('Unable to stop ' + co + ': ' + e.message)
                print ''.join(getPyroTraceback(e))
        
        for co in options.remove:
            try:
                log.debug('Attempting to remove ' + remSite + co)
                rm.remove(co)
                log.info('Started ' + remSite + co)
            except Exception, e:
                log.warning('Unable to remove ' + co + ': ' + e.message)
                print ''.join(getPyroTraceback(e))
        
        if options.list or options.listdetail:
            print rm.URI.__str__()
            for key in rm.getResources():
                ro = self.locManager.getProxy(remSite + key.__str__())
                try:
                    state = ' [' + ro.getState().__str__() + ']'
                except Exception, e:
                    state = ''
                print ' - ' + key.__str__() + state
                if options.listdetail:
                    try:
                        config = ro.__get_config__()
                        for key, value in sorted(config):
                            print '      ' + key + ': ' + value.__str__()
                    except AttributeError:
                        print '      {Config not available}'
                    except Exception, e:
                        print e.message
                
        self.endMe()

    def endMe(self):
        chimera.core.log.setConsoleLevel(logging.WARNING)
        sys.exit()
            
if __name__ == '__main__':

    chimera_man_description = " - Manager"

    def check_includepath (option, opt_str, value, parser):
        if not value or not os.path.isdir (os.path.abspath(value)):
            raise optparse.OptionValueError ("Couldn't found %s include path." % value)
        eval ('parser.values.%s.append ("%s")' % (option.dest, value))

    def check_location (option, opt_str, value, parser):
        try:
            l = Location (value)
        except InvalidLocationException:
            raise optparse.OptionValueError ("%s isnt't a valid location." % value)

        eval ('parser.values.%s.append ("%s")' % (option.dest, value))

    parser = OptionParser(prog="chimera-admin", version=_chimera_version_,
                          description=_chimera_description_+chimera_man_description)

    remman = OptionGroup(parser, "Remote Manager configuration")

    remman.add_option("-H", "--host", action="store", 
                      dest="pyro_host", type="string",
                      help="Host name/IP of manager to connect to"
                      "; [default=%default]",
                      metavar="HOST")

    remman.add_option("-P", "--port", action="store", 
                      dest="pyro_port", type="string",
                      help="Port of manager to connect to"
                      "; [default=%default]",
                      metavar="PORT")
    
    actions = OptionGroup(parser, 'Actions')
    
    actions.add_option('-l', '--list', action='store_true', dest='list',
                       help='Print ChimeraObjects in manager.')
    
    actions.add_option('-L', '--listdetail', action='store_true', dest='listdetail',
                       help='Print ChimeraObjects in manager and their settings.')
    
    actions.add_option('--start', action="callback", callback=check_location,
                       dest="start", type="string",
                       help="start the ChimeraObject specified by LOCATION."
                            "This option may be set many times to start multiple ChimeraObjects",
                       metavar="LOCATION")
    
    actions.add_option('--stop', action="callback", callback=check_location,
                       dest="stop", type="string",
                       help="stop the ChimeraObject specified by LOCATION."
                            "This option may be set many times to stop multiple ChimeraObjects",
                       metavar="LOCATION")
    
    actions.add_option('--remove', action="callback", callback=check_location,
                       dest="remove", type="string",
                       help="remove the ChimeraObject specified by LOCATION."
                            "This option may be set many times to remove multiple ChimeraObjects",
                       metavar="LOCATION")
    
    actions.add_option('--shutdown', action='store_true', dest='shutdown',
                       help='Shutdown the remote manager')
    
    loads   = OptionGroup(parser, 'Load items')

    loads.add_option("-i", "--instrument", action="callback", callback=check_location,
                     dest="instruments", type="string",
                     help="Load the instrument defined by LOCATION."
                          "This option may be set many times to load multiple instruments.",
                     metavar="LOCATION")

    loads.add_option("-c", "--controller", action="callback", callback=check_location,
                     dest="controllers", type="string",
                     help="Load the controller defined by LOCATION."
                          "This option may be set many times to load multiple controllers.",
                     metavar="LOCATION")

    loads.add_option("-d", "--driver", action="callback", callback=check_location,
                     dest="drivers", type="string",
                     help="Load the driver defined by LOCATION."
                          "This option may be set many times to load multiple drivers.",
                     metavar="LOCATION")

    loads.add_option("-I", "--instruments-dir", action="callback", callback=check_includepath,
                     dest="inst_dir", type="string",
                     help="Append PATH to instruments load path.",
                     metavar="PATH")

    loads.add_option("-C", "--controllers-dir", action="callback", callback=check_includepath,
                     dest="ctrl_dir", type="string",
                     help="Append PATH to controllers load path.",
                     metavar="PATH")

    loads.add_option("-D", "--drivers-dir", action="callback", callback=check_includepath,
                     dest="drv_dir", type="string",
                     help="Append PATH to drivers load path.",
                     metavar="PATH")

    parser.add_option("-v", "--verbose", action="count", dest='verbose',
                      help="Increase log level (multiple v's to increase even more).")

    parser.add_option_group(remman)
    parser.add_option_group(actions)
    parser.add_option_group(loads)

    parser.set_defaults(instruments = [],
                        controllers = [],
                        drivers     = [],
                        
                        inst_dir = [],
                        ctrl_dir = [],
                        drv_dir = [],
                        
                        list = False,
                        listdetail = False,
                        start = [],
                        stop = [],
                        remove = [],
                        shutdown = False,
                        
                        verbose = 0,
                                                
                        pyro_host=MANAGER_DEFAULT_HOST,
                        pyro_port=MANAGER_DEFAULT_PORT)
    
    (options, args) = parser.parse_args(sys.argv)
    
    d=doStuff(options,args)
